# Copyright 2009 Lee Harr
#
# This file is part of Acromania.
#
# Acromania is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Acromania is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Acromania.  If not, see <http://www.gnu.org/licenses/>.


import hashlib
import os
import random

import amdb
import util

class Player(object):
    name = 'Player'
    username = ''
    ai = False
    user = None # used for irc players

    def __init__(self, protocol, names):
        self.protocol = protocol
        self.initial_name(names)

    def initial_name(self, names):
        n = len(names)
        name = self.name
        while name in names:
            n += 1
            name = '%s%s' % (self.name, n)
        self.name = name

    def message(self, *args, **kw):
        if self.user is not None:
            kw.update(user=self.user)

        self.protocol.message(*args, **kw)

    def set_password(self, name, pwtext):
        'Save the hashed password.'

        hasher = hashlib.md5()
        hasher.update(pwtext)
        salt = hashlib.md5(os.urandom(4)).hexdigest()[:8]
        hasher.update(salt)
        pwhash = '{pw_v1}' + hasher.hexdigest() + salt

        amdb.setpw(name, pwhash)

    def check_password(self, name, pwtext):
        'Return True if the hash of the given text matches the hashed password.'

        dbhash = amdb.getpw(name)

        if not dbhash:
            return False
        if not dbhash.startswith('{pw_v1}'):
            raise SystemError

        hasher = hashlib.md5()
        hasher.update(pwtext)
        salt = dbhash[-8:]
        hasher.update(salt)
        pwhash = hasher.hexdigest()

        if pwhash == dbhash[7:-8]:
            return True
        else:
            return False


class DummyPlayer(Player):
    def message(self, *args, **kw):
        pass


class Bot(Player):
    name = 'Bot'
    ai = True

    def __init__(self, protocol, names):
        self.initial_name(names)
        import amgame
        self.protocol = amgame.DummyClient()
        self.connectdb()

    def build(self):
        self.createdb()
        self.builddb()

    def connectdb(self):
        import sqlite3
        dbcnn = sqlite3.connect(self.dbname)
        dbcnn.row_factory = sqlite3.Row
        dbcrs = dbcnn.cursor()
        self.dbcnn = dbcnn
        self.dbcrs = dbcrs

    def createdb(self):
        amdb.dbscript('wordschema.sql', cn=self.dbcnn)

    def make_vote(self, acros):
        vote = random.randrange(len(acros))
        return vote


class RBot(Bot):
    'Uses random words from /usr/share/dict'

    name = 'RBot'
    dbname = 'rbotdb'

    def make_acro(self, acro):
        words = []
        for letter in acro:
            word = amdb.dict_word(letter, self.dbcrs)
            words.append(word)

        return ' '.join(words)

    def builddb(self):
        amdb.init_dict_words(self.dbcnn, self.dbcrs)


class CorpusBot(Bot):
    'Uses words from a text corpus.'

    def make_acro(self, acro):
        word1 = word2 = None
        acrowords = []
        for let1, let2 in util.pairwise(acro):
            if word1 is None:
                wordpairs, weights = amdb.get_pairs(let1, let2, crs=self.dbcrs)
            else:
                wordpairs, weights = amdb.get_pairs(let1, let2, word1=word1, crs=self.dbcrs)

            if wordpairs:
                word1, word2 = util.choice_weighted(wordpairs, weights)
            elif word1:
                word2 = None
            else:
                words, weights = amdb.corpus_words(let1, crs=self.dbcrs)
                word1 = util.choice_weighted(words, weights)
                word2 = None

            acrowords.append(word1)
            word1 = word2

        if word2 is None:
            words, weights = amdb.corpus_words(let2, crs=self.dbcrs)
            word2 = util.choice_weighted(words, weights)

        acrowords.append(word2)

        return ' '.join(acrowords)


class Bob(CorpusBot):
    name = 'Bob'
    dbname = 'bobdb'

    def builddb(self):
        amdb.dbopen()
        amdb.pairs_from_hist(self.dbcnn, self.dbcrs)
        amdb.corpus_from_hist(self.dbcnn, self.dbcrs)

class TextCorpusBot(CorpusBot):
    'Uses a text corpus like a book, using nltk to extract words.'

    def builddb(self):
        amdb.make_corpus_from_text(self.text, self.dbcnn, self.dbcrs)

class Alice(TextCorpusBot):
    name = 'Alice'
    dbname = 'alicedb'
    text = 'carroll-alice.txt'

class Bryant(TextCorpusBot):
    name = 'Bryant'
    dbname = 'bryantdb'
    text = 'bryant-stories.txt'

class Milton(TextCorpusBot):
    name = 'Milton'
    dbname = 'miltondb'
    text = 'milton-paradise.txt'

